﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JasLib
{
    /// <summary>
    /// Unlike the .NET string builder, this one doesn't keep reallocating the primary buffer on appends.
    /// </summary>
    public sealed class LargeStringBuilder
    {
        private int _iTotalLengthOfFragments = 0;
        private List<string> _list = new List<string>();
        private StringBuilder _builder = null;
        private string _strFinal = null;

        public void Append(string strNew)
        {
            SwitchToFragmentList();
            _list.Add(strNew);
            _iTotalLengthOfFragments += strNew.Length;
            return;
        }

        public void AppendFormat(string strFormat, params object[] aobjArgs)
        {
            SwitchToFragmentList();
            string strNew = (aobjArgs.Length > 0) ? string.Format(strFormat, aobjArgs) : strFormat;
            _list.Add(strNew);
            _iTotalLengthOfFragments += strNew.Length;
            return;
        }

        public void Prepend(string strNew)
        {
            SwitchToFragmentList();
            _list.Insert(0, strNew);
            _iTotalLengthOfFragments += strNew.Length;
            return;
        }

        public void AppendLineBreak()
        {
            Append(Environment.NewLine);
            return;
        }

        public void AppendLine(string strFormat = null, params object[] aobjArgs)
        {
            AppendFormat(strFormat, aobjArgs);
            AppendLineBreak();
            return;
        }

        public void Clear()
        {
            _list.Clear();
            _strFinal = null;
            _builder = null;
            _iTotalLengthOfFragments = 0;
            return;
        }

        private void SwitchToFragmentList()
        {
            if (_builder != null)
            {
                _list = new List<string>();

                string strBuilder = _builder.ToString();
                _list.Add(strBuilder);
                _iTotalLengthOfFragments += strBuilder.Length;
                _builder = null;
                _strFinal = null;
            }
        }

        private void SwitchToBuilder()
        {
            if (_builder == null)
            {
                _builder = new StringBuilder(_iTotalLengthOfFragments * 2);

                for (int iIndex = 0; iIndex < _list.Count; iIndex++)
                    _builder.Append(_list[iIndex]);

                _list = null;
                _iTotalLengthOfFragments = 0;
            }
        }

        public LargeStringBuilder Insert(int iIndex, string strValue)
        {
            SwitchToBuilder();
            _builder.Insert(iIndex, strValue);
            return this;
        }

        public LargeStringBuilder Remove(int iIndex, int iLength)
        {
            SwitchToBuilder();
            _builder.Remove(iIndex, iLength);
            return this;
        }

        public LargeStringBuilder Replace(string strOldValue, string strNewValue)
        {
            SwitchToBuilder();
            _builder.Replace(strOldValue, strNewValue);
            return this;
        }

        public override string ToString()
        {
            if (_strFinal == null)
            {
                SwitchToBuilder();
                _strFinal = _builder.ToString();
            }
            return _strFinal;
        }
    }
}
